package cn.springcloud.alibaba.auth.config;

import cn.springcloud.alibaba.security.handler.MyAccessDeniedHandler;
import cn.springcloud.alibaba.security.handler.MyAuthenticationEntryPoint;
import cn.springcloud.alibaba.security.handler.MyWebResponseExceptionTranslator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

/**
 * @see org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint 颁发授权码code
 * @see org.springframework.security.oauth2.provider.endpoint.TokenEndpoint  获取token
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private DataSource dataSource;
	@Autowired
	private PasswordEncoder passwordEncoder;
	@Autowired
	private UserDetailsService userDetailsService;
	/**
	 * 注入AuthenticationManager ，密码模式用到
	 */
	@Autowired
	private AuthenticationManager authenticationManager;
	@Autowired
	private MyAccessDeniedHandler myAccessDeniedHandler;
	@Autowired
	private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
	@Autowired
	private MyWebResponseExceptionTranslator myWebResponseExceptionTranslator;

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints
			.tokenStore(jwtTokenStore())
			.authenticationManager(authenticationManager)
			.userDetailsService(userDetailsService)
			.exceptionTranslator(myWebResponseExceptionTranslator)
			.allowedTokenEndpointRequestMethods(HttpMethod.POST)
			.accessTokenConverter(accessTokenConverter());
	}

	/**
	 * 如果支持allowFormAuthenticationForClients的，且对/oauth/token请求的参数中有client_id和client_secret的会走 ClientCredentialsTokenEndpointFilter
	 * 如果不支持allowFormAuthenticationForClients或者有支持但对/oauth/token请求的参数中没有client_id和client_secret的，走basic认证保护
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) {
		security
			.authenticationEntryPoint(myAuthenticationEntryPoint)
			.accessDeniedHandler(myAccessDeniedHandler)
			.passwordEncoder(passwordEncoder)
			.tokenKeyAccess("isAuthenticated()")
			.checkTokenAccess("isAuthenticated()")
			.allowFormAuthenticationForClients();// 允许表单认证
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.withClientDetails(jdbcClientDetailsService());
	}

	@Bean
	public ClientDetailsService jdbcClientDetailsService() {
		JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
		clientDetailsService.setPasswordEncoder(passwordEncoder);
		return clientDetailsService;
	}

	/**
	 * 对Jwt签名时，增加一个密钥
	 * JwtAccessTokenConverter：对Jwt来进行编码以及解码的类
	 */
	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		converter.setSigningKey("test-secret");
		return converter;
	}

	@Bean
	public JwtTokenStore jwtTokenStore() {
		return new JwtTokenStore(accessTokenConverter());
	}
}
